package demo.db.redis;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import demo.vo.CachedObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
 * 
 * spring-data-redis默认使用的序列化方式为JdkSerializationRedisSerializer，当这个redis只在一个project中使用时，这是没问题的。
 * 但是有时候，我们的Redis会在一个项目的多个project中共用，这样如果同一个可以缓存的对象在不同的project中要使用两个不同的key来分别缓存，既麻烦，又浪费。
 * 用JdkSerializationRedisSerializer序列化的话，被序列化的对象必须实现Serializable接口 , 在存储内容时，除了属性的内容外还存了其它内容在里面，总长度长，且不容易阅读。
 * <p>
 * 好在spring-data-redis给我们提供了其它几种序列化方式，其中Json序列化提供了三种：
 * <li>JacksonJsonRedisSerializer，已经加上了@Deprecated注解，随后应该会被删除了
 * <li>Jackson2JsonRedisSerializer
 * <li>GenericJackson2JsonRedisSerializer
 * <p>
 * JacksonJsonRedisSerializer和GenericJackson2JsonRedisSerializer，两者都能系列化成json，
 * 但是GenericJackson2JsonRedisSerializer会在json中加入@class属性，类的全路径包名，方便反系列化。
 * JacksonJsonRedisSerializer如果存放了List则在反系列化的时候如果没指定TypeReference则会报错java.util.LinkedHashMap cannot be cast to xxx。
 * <p>
 * GenericJackson2JsonRedisSerializer这种序列化方式不用自己手动指定对象的Class，使用起来和JdkSerializationRedisSerializer基本一样，
 * 并且JdkSerializationRedisSerializer不能序列化和反序列化不同包路径对象的毛病它也有。因为它序列化之后的内容，是存储了对象的Class信息的。
 *
 */
public class RedisTemplateUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisTemplateUtils.class);

    public static void main(String[] args) {
        JedisConnectionFactory a = null;
    }

    public static JedisPoolConfig buildRedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        return config;
    }

    public static JedisConnectionFactory buildJedisConnectionFactory(String hostName, int port, int db,
            String password) {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setPoolConfig(buildRedisPoolConfig());
        factory.setHostName(hostName);
        factory.setPort(port);
        factory.setDatabase(db);
        factory.setPassword(password);
        factory.setUsePool(true);
        factory.afterPropertiesSet();
        logger.info("JedisConnectionFactory bean init success.");
        return factory;
    }

    public static StringRedisTemplate buildStringRedisTemplate(JedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(factory);
        template.afterPropertiesSet();
        return template;
    }

    public static RedisTemplate<String, Object> buildWithGenericJackson2JsonRedisSerializer(
            JedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置默认的
        template.setDefaultSerializer(genericJackson2JsonRedisSerializer);
        // redis的KEY
        template.setKeySerializer(stringSerializer);
        // hash结构的KEY
        template.setHashKeySerializer(stringSerializer);

        template.setValueSerializer(genericJackson2JsonRedisSerializer);
        template.setHashValueSerializer(genericJackson2JsonRedisSerializer);

        template.setConnectionFactory(factory);
        template.afterPropertiesSet();
        return template;
    }

    public static RedisTemplate<String, CachedObject> buildWithJackson2JsonRedisSerializer(
            JedisConnectionFactory factory) {
        RedisTemplate<String, CachedObject> template = new RedisTemplate<>();
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<CachedObject> serializer = new Jackson2JsonRedisSerializer<>(CachedObject.class);
        // 设置默认的
        template.setDefaultSerializer(serializer);
        // redis的KEY
        template.setKeySerializer(stringSerializer);
        // hash结构的KEY
        template.setHashKeySerializer(stringSerializer);

        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);

        template.setConnectionFactory(factory);
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 
     * 
     * @param factory
     * @return
     */
    public static RedisTemplate<String, Serializable> buildJDKObjectRedisTemplate(JedisConnectionFactory factory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer();
        template.setDefaultSerializer(serializer);
        // redis的KEY
        template.setKeySerializer(stringSerializer);
        // hash结构的KEY
        template.setHashKeySerializer(stringSerializer);

        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);

        template.setConnectionFactory(factory);
        template.afterPropertiesSet();
        return template;
    }


    public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.json());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

}

class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
    private Class<T> clazz;

    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) {
        return Optional.ofNullable(t)
                .map(r -> JSONObject.toJSONString(r, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8))
                .orElseGet(() -> new byte[0]);
    }

    @Override
    public T deserialize(byte[] bytes) {
        return Optional.ofNullable(bytes).map(t -> JSONObject.parseObject(new String(t, StandardCharsets.UTF_8), clazz))
                .orElseGet(() -> null);
    }

}
